﻿namespace MefContrib.Models.Provider
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.Composition.Primitives;
    using System.Diagnostics;
    using System.Linq;
    using System.Reflection;

    /// <summary>
    /// Defines the base functionality of an immutable <see cref="ComposablePartCatalog"/> created
    /// for an <see cref="DefinitionProvider"/>.
    /// </summary>
    public abstract class DefinitionProviderPartCatalog : ComposablePartCatalog, IDisposable
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="DefinitionProviderPartCatalog"/> class.
        /// </summary>
        protected DefinitionProviderPartCatalog()
        {
            DefinitionLocator.Register(this);
            this.Factory = new DefaultObjectFactory();
        }

        /// <summary>
        /// Gets or sets the object factory that the catalog should use.
        /// </summary>
        /// <value>An <see cref="ObjectFactory"/> object.</value>
        public ObjectFactory Factory { get; set; }

        /// <summary>
        /// Gets the definitions for the parts that was discovered by the catalog.
        /// </summary>
        /// <value>An <see cref="IQueryable{ComposablePartDefinition}"/> object.</value>
        public override IQueryable<ComposablePartDefinition> Parts
        {
            [DebuggerStepThrough]
            get { return GetComposablePartDefinitions().Cast<ComposablePartDefinition>().AsQueryable(); }
        }

        /// <summary>
        /// Gets the provider used by the catalog.
        /// </summary>
        /// <value>A <see cref="DefinitionProvider"/> object.</value>
        public DefinitionProvider Provider { get; protected set; }

        /// <summary>
        /// 
        /// </summary>
        public new void Dispose()
        {
            base.Dispose();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="instance"></param>
        /// <returns></returns>
        public ProviderComposablePartDefinition GetDefinition(object instance)
        {
            var matches =
                from definition in GetComposablePartDefinitions()
                where (string.Compare(definition.Type.FullName, instance.GetType().FullName, StringComparison.InvariantCultureIgnoreCase) == 0)
                select definition;

            return matches.FirstOrDefault();
        }

        /// <summary>
        /// Gets a list of all composable part definitions which was discovered by the catalog.
        /// </summary>
        /// <returns>A <see cref="List{T}"/> object.</returns>
        private List<ProviderComposablePartDefinition> GetComposablePartDefinitions()
        {
            var definitions = new List<ProviderComposablePartDefinition>();

            foreach (PartDescription part in Provider.Parts)
            {
                definitions.Add(
                    new ProviderComposablePartDefinition(
                        this,
                        part.TypeName,
                        GetExportDefinitions(part),
                        GetImportDefinitions(part)));
            }

            return definitions;
        }

        /// <summary>
        /// Gets a list of all export definitions which belongs to the part.
        /// </summary>
        /// <param name="part">The <see cref="PartDescription"/> object to get the export definitions form.</param>
        /// <returns>A <see cref="List{T}"/> object.</returns>
        private IEnumerable<ProviderExportDefinition> GetExportDefinitions(PartDescription part)
        {
            var exports =
                new List<ProviderExportDefinition>();

            foreach (ExportDescription export in part.Exports)
            {
                MemberInfo member =
                    MemberInfoServices.GetMemberInfo(part.TypeName, export.MemberName);

                exports.Add(
                    new ProviderExportDefinition(
                        member,
                        export));
            }

            return exports;
        }

        /// <summary>
        /// Gets a list of all import definitions which belongs to the part.
        /// </summary>
        /// <param name="part">The <see cref="PartDescription"/> object to get the import definitions form.</param>
        /// <returns>A <see cref="List{T}"/> object.</returns>
        private IEnumerable<ProviderImportDefinition> GetImportDefinitions(PartDescription part)
        {
            var imports = new List<ProviderImportDefinition>();

            foreach (ImportDescription import in part.Imports)
            {
                MemberInfo member =
                    MemberInfoServices.GetMemberInfo(part.TypeName, import.MemberName);

                imports.Add(
                    new ProviderImportDefinition(
                        member,
                        import));
            }

            return imports;
        }
    }
}